/***************************************************************************
 *   Copyright (C) 2007 by Marco Bonifazi				   *
 *   marcobonifazi@gmail.com 						   *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining * 
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    * 
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/
 
#include "grafi_gl.h"

int in_printing = 0;
grid *current_grid, *start_grid, *final_grid;
config* current_cfg;
int clocks[4];
int first_creation;
int top_row, down_row, left_col, right_col;
float scale = 1.0, scala_quadrati = 0.2;
float x_trasl = 0, y_trasl = 0;
int viewport_width = 1200;
int viewport_height = 700;
int opengl; 
int array_limits[4];
int d_r = -1, d_c = -1;
int row_ext, col_ext;
int rc = 0;

float color_quads[3] = {0.5, 1, 0};
float color_lines[3] = {0.4, 0.4, 1};


void print_file( char* file_name ){
	/* Prints a pdf graph file using gl2ps libraries  */
	in_printing = 1;
	FILE *fp = fopen( file_name, "wb");
	GLint buffsize = 0, state = GL2PS_OVERFLOW;
	GLint viewport[4]={0, 0, viewport_width, viewport_height};
	
	/*glGetIntegerv(GL_VIEWPORT, viewport);
	*/
	while( state == GL2PS_OVERFLOW ){ 
	  buffsize += 2000*1024;
	  gl2psBeginPage ( "A graph", "M.A.G.O.", viewport,
	                   GL2PS_PDF,  GL2PS_NO_SORT,
                            GL2PS_NO_BLENDING|GL2PS_OCCLUSION_CULL|GL2PS_BEST_ROOT|GL2PS_NO_TEXT|GL2PS_NO_PIXMAP ,  GL_RGBA, 0, NULL, 0, 0, 0, buffsize,
	                   fp, "Grafo.pdf" );
	  glPushMatrix();

	  display_grid();
	  glPopMatrix();
	  state = gl2psEndPage();
	}
	
	fclose(fp);
	in_printing = 0;
}

list_arcs* elaborate_display_arcs(grid* gr){
	/* It sets arcs to display them enlarging line when two segments links same nodes */

	list_arcs* c_arcs = NULL;
	
	wide_visit( current_grid, &c_arcs  );
	//printf("Lista degli archi\n");

	prepare_list_arcs(c_arcs);
	int lung = length_list_arcs(c_arcs);
	
	arc* array_arcs = malloc(sizeof(arc) * lung);
	list_arcs* temp_arcs = c_arcs;
	int i = 0;
	
	while ( temp_arcs != NULL){
		array_arcs[i] = *(temp_arcs->arc);
		i += 1;
		temp_arcs = temp_arcs->next;
	
	} 
	free_list_arcs(c_arcs);
	
	qsort (array_arcs, lung, sizeof(arc), (void*)compare_arcs);
	
	i = 0;
	node *n1, *n2, *n_prec_1, *n_prec_2;
	arc temp_arc;
	arc* new_arc;

	list_arcs* rest_arcs = NULL;
	
	while ( i < lung){
		temp_arc = array_arcs[i];
		n1 = temp_arc.n1;
		n2 = temp_arc.n2;
		
		if ( i != 0 )
			if ( (n1->position[0] == n_prec_1->position[0] && n1->position[1] == n_prec_1->position[1] && n2->position[0] == n_prec_2->position[0] && n2->position[1] == n_prec_2->position[1]) ||
					(n1->position[0] == n_prec_2->position[0] && n1->position[1] == n_prec_2->position[1] && n2->position[0] == n_prec_1->position[0] && n2->position[1] == n_prec_1->position[1])){

				new_arc->degree ++;
			}
			else {
				new_arc = create_arc(n1, n2);
				new_arc->degree = 1;
				insert_arc( &rest_arcs, new_arc);
			}
		else{
			new_arc = create_arc(n1, n2);
			new_arc->degree = 1;
			insert_arc( &rest_arcs, new_arc);
		}
		n_prec_1 = n1;
		n_prec_2 = n2;
				
		i ++;
	}
	//free(array_arcs);
	array_arcs = NULL;
	/*print_list_arcs(rest_arcs);*/ 
	//list_real_arcs* temp_real_arcs = create_list_arcs( gr );
	//assign_label_list_arcs( temp_real_arcs );
	//free_list_real_arcs(temp_real_arcs);

	return rest_arcs;
}

void display_arcs(){
	/* Scans arc lists and display lines between nodes */
	glColor3f(color_lines[0], color_lines[1], color_lines[2]);
	list_arcs* temp = elaborate_display_arcs(current_grid);

	float x1, y1, x2, y2;

	glPushMatrix();
	int grid_width, grid_height;
	grid_width = current_grid->dims[0];
	grid_height = current_grid->dims[1];
	
	
	glScalef(1, -1, 1);
	
	trasla(-grid_width, -grid_height/2.0);
	
	
	while ( temp != NULL ){
		glPushMatrix();
		x1 = temp->arc->n1->position[1];
		y1 = temp->arc->n1->position[0];
		x2 = temp->arc->n2->position[1];
		y2 = temp->arc->n2->position[0];
		/* printf("(%f, %f) ,(%f, %f)\n", x1, y1, x2, y2);
		 */
	
		float diff = 0.00;

		float p1[2];
		float p2[2];
		if ( x1 <= x2 && y1 <= y2 ) {
			if (x1 < x2){
				p1[0] = x1 + diff;
				p1[1] = y1;  
				p2[0] = x2 - diff;
				p2[1] = y2;
			}
			else{
				p1[0] = x1; 
				p1[1] = y1 + diff;
				p2[0] = x2; 
				p2[1] = y2 - diff;
			
			}
		}
		else{
			if (x1 > x2){
				p1[0] = x1 - diff;
				p1[1] = y1;  
				p2[0] = x2 + diff;
				p2[1] = y2;
			}
			else{
				p1[0] = x1; 
				p1[1] = y1 - diff;
				p2[0] = x2;
				p2[1] = y2 + diff;
			
			}
		
		}
		
		
		int degree = temp->arc->degree;
		if (!in_printing)
			glLineWidth(degree*degree * 1);
		else{
			gl2psLineWidth( degree*degree  );
			
		}

		/*float larg = 1.0;
		float space = larg / degree / scale;*/
		
		int d;
		
		glPushMatrix();
		/*if ( degree % 2 == 0)
			glTranslatef( -(x1 == x2) * space/2, -(y1 == y2) * space/2, 0  );*/
		//for ( d = 0; d < degree; d ++ ){
			glPushMatrix();

			segment(p1, p2);
			glPopMatrix();
			
			/*glTranslatef( (x1 == x2) * space, (y1 == y2) * space, 0  );*/
		
		
		//}
		
		glPopMatrix();
		
		if (!in_printing)
			glLineWidth( 1);
		else
			gl2psLineWidth(1);

		
		temp = temp->next;
		glPopMatrix();
	}
	glPopMatrix();
};

void display_grid(){
	/* Displays lines of arcs and nodes */
	int grid_width = start_grid->dims[0];
	
	glClear(GL_COLOR_BUFFER_BIT);
	glLoadIdentity();
	float ratio = (float)viewport_width / viewport_height;
	opengl_display( ratio );
	glScalef(scale, scale, 1.0);
	
	
	glPushMatrix();
	
	trasla(x_trasl, y_trasl);
	if (first_creation == 1 ){
		glPushMatrix();
		glNewList(100,GL_COMPILE);	
		current_grid = start_grid;
		display_each_grid();
		
		glTranslatef(grid_width, 0, 0 );
		
		current_grid = final_grid;
		display_each_grid();
		
		first_creation = 0;
		glEndList();
		glPopMatrix();
		glCallList(100);
	}
	else if (in_printing){
		glPushMatrix();
		current_grid = start_grid;
		display_each_grid();
		
		glTranslatef(grid_width, 0, 0 );
		
		current_grid = final_grid;
		display_each_grid();
		glPopMatrix();
	
	
	}
	else{
		glCallList(100);
	}
	glPopMatrix();
	glutSwapBuffers(); 
}

void display_each_grid(){

	if (current_grid != NULL){

	    glPushMatrix();
	    
	    
	    int r = 0;
	    int c = 0;
	     
	    int grid_width = current_grid->dims[0];
	    int grid_height = current_grid->dims[1];
		
	    display_arcs();
		
	    glPushMatrix();
	    
	    trasla(-grid_width, +grid_height/2.0);
	    
	    for (r = 0; r < grid_height; r++){
		glColor3f(color_quads[0], color_quads[1], color_quads[2]);
	
		
	        for (c=0; c<grid_width; c++){
			glColor3f(color_quads[0], color_quads[1], color_quads[2]);
		   
	           if (current_grid->cells[r][c] != NULL && current_grid->cells[r][c]->node->type == 1){
	               glPushMatrix();
	               glTranslatef(c, -r, +1);
	              // trasla((c), (-r) );
	               glScalef(scala_quadrati, scala_quadrati, 1.0);
	               quadrato();
	      
	               glPopMatrix();
	           };
	        }
	    }
	    glPopMatrix();
	}
	
	
}

void press_key(unsigned char key, int x, int y){
	/* OpenGl "press key" function, you can navigate in the grid with numeric keys and print the graphwith 's'*/


	
	if (key == '5'){
		scale = scale + 0.5;
	}
	else if (key == '0'){
		scale = scale - 0.5;
	}
	else if (key == '2'){
		y_trasl = y_trasl - 0.5;
	}
	else if (key == '4'){
		x_trasl = x_trasl - 0.5;
	}
	else if (key == '8'){
		y_trasl = y_trasl + 0.5;
	}
	else if (key == '6'){
		x_trasl = x_trasl + 0.5;
	}

	else if ( key == 's' ){
		print_file("grafo.pdf" );
	}
	glutPostRedisplay();
	
}

void nulla(){
    glLoadIdentity();
    glClear(GL_COLOR_BUFFER_BIT); /* Clears the drawing area*/
    glColor3f(1.0,1.0,1.0);

   // glutSwapBuffers();      
}

void init_gl(int argc, char** argv, grid* start_grid, grid* current_grid, config* current_cfg){
	first_creation = 1; /* Used to handle calllist instead of regenerate each redisplay of the same figure*/
	start_grid = start_grid;
	current_grid = current_grid;
	final_grid = current_grid;
	current_cfg = current_cfg;
	glutInit(&argc,  argv);
	int width = start_grid->dims[0];
	int height = start_grid->dims[1];
   	opengl_init(viewport_width, viewport_height, width+2, height+2);
	glutDisplayFunc( display_grid ); 
	glutKeyboardFunc( press_key );
	glutMainLoop();	/* Event processing loop, it must be the last statement*/
}
